// Filename: directd.h
// Created by:  skyler 2002.04.08
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include <process.h>
#include "pandabase.h"
#include "directsymbols.h"
#include "queuedConnectionManager.h"
#include "queuedConnectionReader.h"
#include "connectionWriter.h"
#include "queuedConnectionListener.h"
#include <windows.h>


#ifdef CPPPARSER //[
// hack for interrogate
typedef int intptr_t;
typedef int HANDLE;
#endif //]


// Description: DirectD is a client/server app for starting panda/direct.
//              
//              Usage:
//              Start a directd server on each of the machines you
//              which to start panda on.
//              
//              Start a directd client on the controlling machine or 
//              import ShowBaseGlobal with the xxxxx flag in your
//              Configrc.  The client will connect each of the servers 
//              in the xxxxx list in your Configrc.
//              
//              There are two API groups in this class, they are:
//              
//                listen_to()
//                client_ready() or tell_server()
//                wait_for_servers()
//                server_ready()
//              
//              and:
//              
//                connect_to()
//                send_command()
//                disconnect_from()
//              
//              The second group was from a more general implementation
//              of DirectD.  The first group summarizes the main intents
//              of DirectD.
//              Both groups are presented in order chronologically by their
//              intended usage.
//              The first group will probably provide everthing needed for
//              DirectD.
class EXPCL_DIRECT DirectD {
PUBLISHED:
  DirectD();
  ~DirectD();
  
  // Description: Call listen_to in the server.
  //              port is a rendezvous port.
  //              
  //              backlog refers to how many connections can queue up
  //              before you handle them.  Consider setting backlog to
  //              the count you send to wait_for_servers(); or higher.
  void listen_to(int port, int backlog=8);

  // Description: Call this function from the client when
  //              import ShowbaseGlobal is nearly finished.
  //              cmd: a cli command that will be executed on the remote
  //                   machine.
  //              A new connection will be created and closed.  If you
  //              want to send more than one command, you should use
  //              connect_to(), send_command(), and disconnect_from().
  int client_ready(const string& server_host, int port, const string& cmd);

  // Description: Tell the server to do the command cmd.
  //              cmd is one of the following:
  //                "k[<n>]"    Kill the most recent application 
  //                            started with client_ready() or "!".
  //                            Or kill the nth most recent or 'a' for All.
  //                            E.g. "k", "k0", "k2", "ka".
  //                "q"         Tell the server to quit.
  //                "!cmd"      Exectue the cmd on the server (this
  //                            is a dos shell command; if you want
  //                            a bash command, include bash in the
  //                            command e.g. "!bash pwd").  When you call
  //                            client_ready(), it prefixes "!" for you.
  //              A new connection will be created and closed.
  int tell_server(const string& server_host, int port, const string& cmd);

  // Description: Call this function from the client after
  //              calling <count> client_ready() calls.
  //              
  //              Call listen_to(port) prior to calling
  //              wait_for_servers() (or better yet, prior
  //              to calling client_ready()).
  //              
  //              timeout_ms defaults to two minutes.
  bool wait_for_servers(int count, int timeout_ms=2*60*1000);

  // Description: Call this function from the server when
  //              import ShowbaseGlobal is nearly finished.
  int server_ready(const string& client_host, int port);
  
  // Description: Call connect_to from client for each server.
  //              returns the port number of the connection (which
  //              is different from the rendezvous port used in the
  //              second argument).  The return value can be used
  //              for the port arguemnt in disconnect_from().
  int connect_to(const string& server_host, int port);
  
  // Description: This is the counterpart to connect_to().  Pass
  //              the same server_host as for connect_to(), but pass
  //              the return value from connect_to() for the port,
  //              not the port passed to connect_to().
  void disconnect_from(const string& server_host, int port);
  
  // Description: Send the same command string to all current
  //              connections.
  void send_command(const string& cmd);

protected:
  void start_app(const string& cmd);
  void kill_app(int index);
  void kill_all();
  virtual void handle_command(const string& cmd);
  void handle_datagram(NetDatagram& datagram);
  void send_one_message(const string& host_name, 
      int port, const string& message);

  QueuedConnectionManager _cm;
  QueuedConnectionReader _reader;
  ConnectionWriter _writer;
  QueuedConnectionListener _listener;

  // Start of old stuff:
  // This is used to switch to the original method of 
  // starting applications.  It can be used on old systems
  // that don't support job objects.  Eventually this stuff
  // should be removed.
  bool _useOldStuff;
  typedef pvector< long /*intptr_t*/ > PidStack;
  PidStack _pids;
  // End of old stuff

  typedef pset< PT(Connection) > ConnectionSet;
  ConnectionSet _connections;
  HANDLE _jobObject;
  bool _shutdown;
  
  void check_for_new_clients();
  void check_for_datagrams();
  void check_for_lost_connection();
};
